﻿using System;
using System.Net;
using System.Text;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Tcp;
using UtilZ.Dotnet.Ex.DataStruct;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;

namespace WinFormApp
{
    public partial class FTcpClient : Form
    {
        private readonly TestNetTcpListenerUnpacker _testNetTcpListenerUnpacker = new TestNetTcpListenerUnpacker();
        public FTcpClient()
        {
            InitializeComponent();
        }


        private ThreadEx _transferSpeedThread = null;
        private ThreadEx _sendDataThread = null;
        private long _transferDataLength = 0L;
        private AsynQueue<TestOutputDataBuffer> _dataSendAsynQueue = null;
        private readonly SingleTypeObjectPool<TestOutputDataBuffer> _pool = new SingleTypeObjectPool<TestOutputDataBuffer>();
        private void TTecpClient_Load(object sender, EventArgs e)
        {
            try
            {
                var redirectAppenderToUI = (RedirectAppender)Loger.GetAppenderByName(null, "RedirectToUI");
                if (redirectAppenderToUI != null)
                {
                    redirectAppenderToUI.RedirectOuput += RedirectLogOutput;
                }

                checkBoxLockLog.Checked = logControl.IsLock;
                checkBoxLockLog.CheckedChanged += CheckBoxLockLog_CheckedChanged;

                this._transferSpeedThread = new ThreadEx(this.TransferSpeedThreadMethod);
                this._sendDataThread = new ThreadEx(this.SendDataThreadMethod);
                this._testNetTcpListenerUnpacker.PackOutput += _testNetTcpListenerUnpacker_PackOutput;

                var option = new AsynQueueOptions()
                {
                    AutoStart = true,
                    Capcity = 1000,
                    FullMode = ContainerFullMode.DropOld
                };
                this._dataSendAsynQueue = new AsynQueue<TestOutputDataBuffer>(this.DataSendAsynQueueCallback, option);
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }


        private void CheckBoxLockLog_CheckedChanged(object sender, EventArgs e)
        {
            logControl.IsLock = checkBoxLockLog.Checked;
        }

        private void RedirectLogOutput(object sender, RedirectOuputArgs e)
        {
            logControl.AddLog($"{e.Item.Time.ToString("yyyy-MM-dd HH:mm:ss.fff")} {e.Item.Content}", e.Item.Level);
        }

        private void btnClearLog_Click(object sender, EventArgs e)
        {
            logControl.Clear();
        }






        private NetTcpClient _connectClient = null;
        private void btnCoinnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._connectClient == null)
                {
                    var ip = IPAddress.Parse(txtServerIp.Text);
                    IPEndPoint endPoint = new IPEndPoint(ip, (int)numServerPort.Value);
                    var para = new NetTcpClientInitPara(this._testNetTcpListenerUnpacker, endPoint)
                    {
                        ReconnectIntervalMilliseconds = 2000,
                        BufferSize = 4100,
                        ReceiveDataSize = 4096,
                        ReceiveDataMode = ThreadMode.Sync,
                        //SendMillisecondsTimeout = 100,
                        //ReceiveMillisecondsTimeout = 2000,

                        KeepAliveTimeSeconds = 3,
                        KeepAliveIntervalSeconds = 1,
                        KeepRetryCount = 3,
                        KeepAlive = true,
                    };
                    this._connectClient = new NetTcpClient(para);
                    this._connectClient.ConnectChanged += ConnectClient_ConnectChanged;
                }

                if (this._connectClient.Connected)
                {
                    this._connectClient.Disconnect();
                }
                else
                {
                    this._connectClient.ConnectAsync();
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void ConnectClient_ConnectChanged(object sender, NetConnectedChangedArgs e)
        {
            this.Invoke(new Action(() =>
            {
                if (e.Status == ConnectionStatus.Opened)
                {
                    btnCoinnect.Text = "断开";
                    Loger.Info($"连接到\"{NetHelper.GetIPPortString(e.RemoteEndPoint)}\"");
                }
                else
                {
                    btnCoinnect.Text = "连接";
                    Loger.Warn($"与\"{NetHelper.GetIPPortString(e.RemoteEndPoint)}\"断开");
                }
            }));
        }



        private NetTcpListener _tcpListener = null;
        private NetTcpClient _acceptClient = null;
        //private List<NetTcpClient> _acceptTcpClientList = new List<NetTcpClient>();
        private readonly object _acceptTcpClientListLock = new object();
        private void btnListen_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._tcpListener == null)
                {
                    var initPara = new NetTcpListenerInitPara((int)numListenPort.Value, 3);
                    this._tcpListener = new NetTcpListener(this.AcceptSocket, initPara);
                }

                if (this._tcpListener.Accepting)
                {
                    this._tcpListener.Stop();
                    this._tcpListener.Dispose();
                    this._tcpListener = null;

                    if (this._acceptClient != null)
                    {
                        this._acceptClient.ConnectChanged -= AcceptClient_ConnectChanged;
                        this._acceptClient.Dispose();
                        this._acceptClient = null;
                    }

                    //lock (this._acceptTcpClientListLock)
                    //{
                    //    foreach (var client in this._acceptTcpClientList)
                    //    {
                    //        client.ConnectChanged -= Client_ConnectChanged;
                    //        client.Dispose();
                    //    }
                    //    this._acceptTcpClientList.Clear();
                    //}

                    btnListen.Text = "监听";
                    Loger.Info("停止监听");
                }
                else
                {
                    this._tcpListener.Start();
                    btnListen.Text = "停止";
                    Loger.Info("开始监听");
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex, "监听操作失败");
            }
        }


        private void AcceptSocket(AcceptSocketArgs args)
        {
            var initPara = new NetTcpClientInitPara(this._testNetTcpListenerUnpacker, args)
            {
                KeepAliveTimeSeconds = 3,
                KeepAliveIntervalSeconds = 1,
                KeepRetryCount = 3,
                KeepAlive = true,
            };
            var client = new NetTcpClient(initPara);
            client.ConnectChanged += AcceptClient_ConnectChanged;
            //lock (this._acceptTcpClientListLock)
            //{
            //    this._acceptTcpClientList.Add(client);
            //}

            this._acceptClient = client;
            Loger.Info($"监听到连接\"{NetHelper.GetIPPortString(client.LinkInfo.RemoteEndPoint)}\"");
        }

        private void AcceptClient_ConnectChanged(object sender, NetConnectedChangedArgs e)
        {
            try
            {
                var client = (NetTcpClient)sender!;
                switch (e.Status)
                {
                    case ConnectionStatus.Opened:
                        Loger.Info($"客户端\"{NetHelper.GetIPPortString(client.LinkInfo.RemoteEndPoint)}\"连接已建立...");
                        break;
                    case ConnectionStatus.Closed:
                        client.ConnectChanged -= AcceptClient_ConnectChanged;
                        client.Dispose();

                        //lock (this._acceptTcpClientListLock)
                        //{
                        //    this._acceptTcpClientList.Remove(client);
                        //}
                        if (this._acceptClient == client)
                        {
                            this._acceptClient = null;
                        }
                        Loger.Warn($"客户端\"{NetHelper.GetIPPortString(client.LinkInfo.RemoteEndPoint)}\"连接已断开...");
                        break;
                    default:
                        Loger.Warn($"未知的连接状态\"{e.Status.ToString()}\"");
                        break;
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }


        private NetTcpClient GetNetTcpClient()
        {
            if (this._connectClient == null)
            {
                if (this._acceptClient == null)
                {
                    throw new InvalidOperationException("未连接到服务端或未监听到连接");
                }
                else
                {
                    return this._acceptClient;
                }
            }
            else
            {
                if (this._connectClient.Connected)
                {
                    return this._connectClient;
                }
                else
                {
                    throw new InvalidOperationException("未连接到服务端");
                }
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes($"abc123叶枫{TimeEx.GetTimestamp()}");
                this.GetNetTcpClient().Send(buffer);
                Loger.Info("发送成功");
            }
            catch (Exception ex)
            {
                Loger.Error(ex, "发送失败");
            }
        }

        private void btnSend2_Click(object sender, EventArgs e)
        {

            if (this._sendDataThread.IsRuning)
            {
                btnSend2.Text = "连续发送";
                this._sendDataThread.Stop();
            }
            else
            {
                btnSend2.Text = "停止发送";
                this._sendDataThread.Start((int)numSendBufferLength.Value);
            }
        }


        private bool _sendToQueue = false;
        private long _overflowCount = 0;
        private void SendDataThreadMethod(ThreadPara threadPara)
        {
            try
            {
                var client = this.GetNetTcpClient();
                if (client == null)
                {
                    return;
                }

                int numSendBufferLength = (int)threadPara.Para;
                byte[] buffer = new byte[numSendBufferLength];
                TestOutputDataBuffer data;
                List<TestOutputDataBuffer> removeList;

                while (!threadPara.Token.IsCancellationRequested)
                {
                    if (this._sendToQueue)
                    {
                        if (!this._pool.TryTake(out data))
                        {
                            data = new TestOutputDataBuffer(numSendBufferLength);
                        }
                        data.WriteData(buffer);

                        removeList = this._dataSendAsynQueue.Enqueue(data);
                        if (removeList != null)
                        {
                            this._pool.Add(removeList);
                            Interlocked.Increment(ref this._overflowCount);
                        }
                    }
                    else
                    {
                        client.Send(buffer);
                        Interlocked.Add(ref this._transferDataLength, buffer.Length);
                    }
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void DataSendAsynQueueCallback(TestOutputDataBuffer data, object obj)
        {
            try
            {
                this.GetNetTcpClient().Send(data.BufferData.Bytes);
                Interlocked.Add(ref this._transferDataLength, data.BufferData.Length);
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }


        private void _testNetTcpListenerUnpacker_PackOutput(object sender, DataOutputArgs e)
        {
            Interlocked.Add(ref this._transferDataLength, ((TestNetTcpDataOutputArgs)e).DataLength);
        }




        private void TransferSpeedThreadMethod(ThreadPara threadPara)
        {
            try
            {
                int interval = (int)threadPara.Para;
                long lastTimestamp = TimeEx.GetTimestamp();
                long overflowCount;
                while (!threadPara.Token.IsCancellationRequested)
                {
                    Loger.Info("---------------------------------");
                    var dataLength = Interlocked.Exchange(ref this._transferDataLength, 0L);
                    var duration = TimeEx.GetTimestamp() - lastTimestamp;
                    Loger.Info($"tcp数据传输速度:{(double)dataLength / (duration * 1000)}MB/s.");
                    lastTimestamp = TimeEx.GetTimestamp();

                    overflowCount = Interlocked.Exchange(ref this._overflowCount, 0L);
                    Loger.Info($"{(double)duration / 1000}/s溢出{overflowCount}项.");

                    threadPara.WaitOne(interval);
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void btnStaticOutput_Click(object sender, EventArgs e)
        {
            if (this._transferSpeedThread.IsRuning)
            {
                this._transferSpeedThread.Stop();
            }
            else
            {
                this._transferSpeedThread.Start((int)(numStaticInterval.Value * 1000));
            }
        }




        private void checkBoxQueueSend_CheckedChanged(object sender, EventArgs e)
        {
            this._sendToQueue = checkBoxQueueSend.Checked;
        }
    }

    internal class TestNetTcpListenerUnpacker : UnpackerAbs
    {
        private readonly TestNetTcpDataOutputArgs _args = new TestNetTcpDataOutputArgs();

        public TestNetTcpListenerUnpacker()
        {

        }

        private long _toltao = 0;
        private long _lastTimestamp = TimeEx.GetTimestamp();
        protected override int PrimitiveUnPackge(IUnpackerPara unpackerPara)
        {
            //var netPara = (NetTcpUnpackerPara)unpackerPara;
            //string str = Encoding.UTF8.GetString(netPara.Buffer, netPara.Offset, netPara.DataLength);
            //Loger.Info($"收到数据\"{str}\"");            
            //return netPara.DataLength;

            //this._args.DataLength = unpackerPara.DataLength;
            //base.OnRaiseOnReceivedData(unpackerPara.Client, this._args);
            _toltao += unpackerPara.DataLength;

            if (TimeEx.GetTimestamp() - _lastTimestamp > 1000)
            {
                Loger.Info($"{_toltao}B/s");
                _toltao = 0;
                _lastTimestamp = TimeEx.GetTimestamp();
            }

            return unpackerPara.DataLength;
        }
    }

    internal class TestNetTcpDataOutputArgs : DataOutputArgs
    {
        public int DataLength { get; set; }

        public TestNetTcpDataOutputArgs()
            : base()
        {

        }

    }

    internal class TestOutputDataBuffer : IDisposable
    {
        private byte[] _buffer = null;
        private Stream _bufferStream = null;
        private BinaryWriter _bufferWriter = null;
        private DataSource _bufferData = null;

        public DataSource BufferData
        {
            get { return this._bufferData; }
        }


        private UInt32 _task = 0;
        /// <summary>
        /// 当前数据Task
        /// </summary>
        public UInt32 Task
        {
            get { return this._task; }
        }


        private bool _isBusinessData = false;
        /// <summary>
        /// 指示数据是否为业务数据[true:业务数据;false:非业务数据]
        /// </summary>
        public bool IsBusinessData
        {
            get { return this._isBusinessData; }
        }

        /// <summary>
        /// 非业务数据文本
        /// </summary>
        public string Text { get; private set; }



        private static int _index = 0;


        public int ID { get; private set; }



        /// <summary>
        /// 构造函数
        /// </summary>
        public TestOutputDataBuffer()
        {
            this.ID = Interlocked.Increment(ref _index);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="length">初始化数据长度</param>
        public TestOutputDataBuffer(int length)
            : this()
        {
            this.CheckBufferLength(length);
        }

        private void CheckBufferLength(long length)
        {
            if (this._buffer == null || this._buffer.Length < length)
            {
                this._buffer = new byte[length];
                if (this._bufferStream != null)
                {
                    this._bufferStream.Dispose();
                    this._bufferWriter.Dispose();
                }
                this._bufferStream = new MemoryStream(this._buffer);
                this._bufferWriter = new BinaryWriter(this._bufferStream);
                this._bufferData = new DataSource(this._buffer);
            }
        }

        public void CopytTo(TestOutputDataBuffer outputData)
        {
            int length = this._bufferData.Length;
            outputData.CheckBufferLength(length);
            Buffer.BlockCopy(this._buffer, 0, outputData._buffer, 0, length);
            outputData._bufferData.Update(0, length);

            outputData._task = this._task;
            outputData._isBusinessData = this._isBusinessData;
        }

        internal void WriteData(byte[] data)
        {
            int length = data.Length;
            if (length < 0)
            {
                throw new ArgumentException($"缓存数据长度为负数:{length},数据对象:{data.GetType().FullName},数据:\r\n{UtilZ.Dotnet.Ex.Json.JsonSerializer.SerializeObject(data)}");
            }
            this.CheckBufferLength(length);


            this._bufferWriter.BaseStream.Position = 0L;
            Array.Copy(data, 0, this._buffer, 0, data.Length);
            this._bufferData.Update(0, length);
        }



        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this._bufferStream != null)
            {
                this._bufferStream.Dispose();
                this._bufferWriter.Dispose();
                this._bufferStream = null;
                this._bufferWriter = null;
            }

        }


    }
}